/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import path from 'path';

import { cloneDeep, parseInt } from '@baize/wdk';
import { hvigorCore, FileSet, TaskInputValue, replaceBundleName, Input } from '@ohos/hvigor';
import * as fs from 'fs-extra';
import fse, { readJSONSync } from 'fs-extra';

import { projectOhosConfigManager } from '../common/global/project-ohos-config-manager.js';
import { moduleDependencyUtil } from '../common/module-dependency-util.js';
import { BuildDirConst } from '../const/build-directory-const.js';
import { BuildModeConst } from '../const/build-mode-const.js';
import { BundleType, CommonConst, DefaultTargetConst, HvigorConfigConst } from '../const/common-const.js';
import { ApiVersion } from '../const/sdk-const.js';
import { ModuleType } from '../enum/module-type-enum.js';
import { ProjectModelImpl } from '../model/project/project-model-impl.js';
import { AppRes, ModuleTargetRes } from '../model/res/res-model.js';
import { TargetSourceSetModel } from '../model/source-set/source-set-model.js';
import { ProjectBuildProfile } from '../options/build/project-build-profile.js';
import { AppJson } from '../options/configure/app-json-options.js';
import { ModuleJson } from '../options/configure/module-json-options.js';
import { Named } from '../options/options.js';
import { OhosLogger } from '../utils/log/ohos-logger.js';
import { AbstractMergeProfile, INPUT_KEY } from './abstract-merge-profile.js';
import { TaskNames } from './common/task-names.js';
import { ModuleTargetData } from './data/hap-task-target-data.js';
import { PreBuild } from './pre-build.js';
import { TargetTaskService } from './service/target-task-service.js';

import AppObj = AppJson.AppObj;
import AppOptObj = AppJson.AppOptObj;
import MetadataObj = ModuleJson.MetadataObj;
import ModuleObj = ModuleJson.ModuleObj;
import ModuleOptObj = ModuleJson.ModuleOptObj;
import RequestPermissionObj = ModuleJson.RequestPermissionObj;
import Task = TaskNames.Task;
import ProductBuildOpt = ProjectBuildProfile.ProductBuildOpt;

import { AppOpt } from '../options/build/app-ohos-config.js';
import { ModuleBuildProfile } from '../options/build/module-build-profile.js';
import { getElementFromArr } from '../utils/array-util.js';
import { InjectUtil } from '../utils/inject-util.js';

import AbilityBuildOpt = ModuleBuildProfile.AbilityBuildOpt;


import { HarTargetUtil } from '../utils/har-target-util.js';
import { getJson5Obj, mergeStringJson } from '../utils/json-util.js';

import ProxyDataObj = ModuleJson.ProxyDataObj;

import { HvigorConfigLoader } from '@ohos/hvigor/src/common/util/hvigor-config-loader.js';

import { Dependency, DependencyType } from '../project/dependency/core/dependency-interface.js';
import { HspTargetUtils } from '../utils/hsp-target-utils.js';
import { getOhosTestAbilityConfig, isTemplatesAfterSourceCodeMigration } from '../utils/ohos-test-util.js';
import { getBuildModeName } from '../utils/task-util.js';
import { DependencyManager } from '../project/dependency/dependency-manager.js';

interface HarModuleOpt {
  app: AppObj;
  module: ModuleObj;
}

const _log: OhosLogger = OhosLogger.getLogger('hvigor-merge-profile');
type MetaDataNameType = string | undefined;

/**
 * 合并app.json和module.json5,合并依赖的har中的module.json5
 *
 * @since 2022/1/10
 */
export class MergeProfile extends AbstractMergeProfile {
  private _appRes: AppRes;
  private _moduleTargetRes: ModuleTargetRes;
  private readonly _isHarModule: boolean;
  private readonly _multiProjects: boolean;
  private _ohLibs: string[] = [];
  private readonly _mergedModuleJson: string;
  private readonly _moduleBuildAbilities: AbilityBuildOpt[] | undefined;

  constructor(taskService: TargetTaskService) {
    super(taskService, Task.MERGE_PROFILE);
    this._isHarModule = this.service.getModuleModel().isHarModule();
    this._multiProjects = this._projectModel.getProfileOpt().app.multiProjects || false;

    this._appRes = (this._projectModel as ProjectModelImpl).getAppRes();
    const targetSourceSetModel: TargetSourceSetModel = this.service.getModuleModel()!.getSourceSetByTargetName(this._targetName) as TargetSourceSetModel;
    this._moduleTargetRes = targetSourceSetModel.getModuleTargetRes();
    this._mergedModuleJson = this._pathInfo.getIntermediatesMergeProfile();
    this._moduleBuildAbilities = getElementFromArr(this.moduleModel.getProfileOpt().targets, this.targetName)?.source?.abilities;
  }

  declareInputs(): Map<string, TaskInputValue> {
    return super
      .declareInputs()
      .set(INPUT_KEY.isHarModule, this._isHarModule)
      .set(INPUT_KEY.multiProjects, this._multiProjects)
      .set(INPUT_KEY.asanEnable, this.asanEnable)
      .set(INPUT_KEY.hwasanEnable, this.hwAsanEnable)
      .set(INPUT_KEY.ubsanEnable, this.ubsanEnable)
      .set(INPUT_KEY.isDebug, this.targetService.isDebug())
      .set(INPUT_KEY.buildProfileAbilities, JSON.stringify(this._moduleBuildAbilities))
      .set(INPUT_KEY.projectConfigAppOpt, JSON.stringify(projectOhosConfigManager.getOverrides()?.appOpt))
      .set(INPUT_KEY.moduleJsonOpt, JSON.stringify(this._moduleTargetRes.getModuleJsonOpt()))
      .set(INPUT_KEY.appJsonOpt, JSON.stringify(this._appRes.getAppResOpt()))
      .set('integratedHsp', !!this.targetService.getBuildOption().arkOptions?.integratedHsp)
      .set('removePermissions', JSON.stringify(this.targetService.getBuildOption()?.removePermissions))
      .set('isBundledDependencies', this.isBundledDependencies)
      .set('packageName', this.packageJsonObj.name ?? '')
      .set('ohLibs', this._ohLibs);
  }

  @Input() private get includeAppScopeRes() {
    return this.targetService.getBuildOption().resOptions?.includeAppScopeRes;
  }

  declareInputFiles(): FileSet {
    const inputFiles = new FileSet()
      .addEntry(this._appRes.getJsonPath())
      .addEntry(this.projectModel.getProfilePath())
      .addEntry(this._moduleTargetRes.getJsonPath());
    this._ohLibs.forEach((harModuleJson) => {
      if (fs.existsSync(harModuleJson)) {
        inputFiles.addEntry(harModuleJson);
      }
    });
    return inputFiles;
  }

  declareOutputFiles(): FileSet {
    return new FileSet().addEntry(this._mergedModuleJson);
  }

  async beforeAlwaysAction(): Promise<void> {
    await super.beforeAlwaysAction();
    this._ohLibs = this.mergeDependsLibs(CommonConst.MODULE_JSON);
  }

  initTaskDepends() {
    this.declareDepends(PreBuild.name);
  }

  protected initHarModuleDepends() {
    const depHarTargets = HarTargetUtil.calDepHarTargets(this.targetService);
    depHarTargets.forEach((targetName, harName) => this.declareDepends(`${targetName}@${Task.MERGE_PROFILE.name}`, harName));
  }

  protected async doTaskAction(): Promise<void> {
    const appJsonOpt = this.computeAppJsonOpt();

    // 用项目配置覆盖模块的bundleName和api版本信息
    this.mergeDslConfig(appJsonOpt);
    this.mergeAppEnvironmentConfig(appJsonOpt);

    // 拿到当前模块的module.json对应的json对象
    const moduleOpt: ModuleOptObj = cloneDeep(this._moduleTargetRes.getModuleJsonOpt());

    if (
      this.targetName === DefaultTargetConst.OHOS_TEST_TARGET &&
      isTemplatesAfterSourceCodeMigration(path.resolve(this._moduleTargetRes.getResourcePath(), '..'))
    ) {
      moduleOpt.module = {
        ...getOhosTestAbilityConfig(),
        ...moduleOpt.module,
      };
    }

    // 在产物的module.json中添加packageName字段
    Object.assign(moduleOpt.module, {
      packageName: this.packageJsonObj.name,
    });

    const afterMergeBuildAbilities = this.mergeBuildProfileAbilities(moduleOpt);

    // 对hap或者hsp进行需要删除权限的获取
    const removePermissionNameSet =
      this.moduleModel.isHspModule() || this.moduleModel.isHapModule() ? this.getRemovePermissionSet(afterMergeBuildAbilities) : new Set();

    // 合并所有引用三方 (har以及hsp) 包的module中的部分配置，包括metadata和requestPermission
    const moduleOptAfterMergeAllHarModuleOpt = this.mergeAllHarModuleOpt(afterMergeBuildAbilities);

    // 根据用户配置的热，removePermission删除三方包中的requestPermission
    if (moduleOptAfterMergeAllHarModuleOpt?.module?.requestPermissions && removePermissionNameSet.size > 0) {
      moduleOptAfterMergeAllHarModuleOpt.module.requestPermissions = moduleOptAfterMergeAllHarModuleOpt.module.requestPermissions?.filter((permission) => {
        return !removePermissionNameSet?.has(permission.name);
      });
    }

    if (fse.existsSync(this.pathInfo.getResourceStr())) {
      fs.rmSync(this.pathInfo.getResourceStr(), { recursive: true, force: true });
    }

    const requestPermissions = moduleOptAfterMergeAllHarModuleOpt.module.requestPermissions;
    this.processResourceStr(requestPermissions);

    moduleOpt.module.proxyData?.forEach((proxyDataObj: ProxyDataObj) => {
      if (this._bundleName) {
        // uri:datashareproxy://bundleName/.../...
        const uriBundleName = proxyDataObj.uri.split('/')[2];
        if (this._appRes.getAppResOpt().app.bundleName === uriBundleName) {
          proxyDataObj.uri = proxyDataObj.uri.replace(uriBundleName, this._bundleName);
        }
      }
    });

    const mergedModuleOpt: AppOptObj & ModuleOptObj = {
      ...appJsonOpt,
      ...moduleOptAfterMergeAllHarModuleOpt,
    };

    // 定制处理product中部分字段配置
    this.customizeModuleOpt(mergedModuleOpt, this.targetData.getProduct());

    // 如果当前模块时hsp且配置了integratedHsp为true则将module.json中的的bundleName置空
    const integratedHsp = this.targetService.getBuildOption().arkOptions?.integratedHsp;
    if (this.moduleModel.isHspModule() && integratedHsp) {
      mergedModuleOpt.app.bundleName = '';
    }

    // 如果当前的模块为hsp并且设置里includeAppScopeRes为false是，要删除app.json中的icon、label字段
    if (this.moduleModel.isHspModule() && this.includeAppScopeRes === false) {
      const { icon, label, ...restApp } = mergedModuleOpt.app;
      mergedModuleOpt.app = restApp;
    }

    // 如果配置了ohos.pack.compressLevel,则将module.json中的的compressNativeLibs置为true
    const pressLevel = HvigorConfigLoader.getInstance().getPropertiesConfigValue<string>(HvigorConfigConst.OHOS_PACK_COMPRESS_LEVEL);
    if (pressLevel) {
      mergedModuleOpt.module.compressNativeLibs = true;
    }

    if (fse.existsSync(this._mergedModuleJson)) {
      const oldModuleJson: AppJson.AppOptObj = getJson5Obj(this._mergedModuleJson);
      // 外部模块且包名不一样，需要删除build缓存
      if (this.moduleModel.isOutModule() && oldModuleJson.app.bundleName !== mergedModuleOpt.app.bundleName) {
        _log.debug('Clean the ArkTS cache due to bundleName is changed.');
        const cachePath: string = path.resolve(this.targetData.getPathInfo().getModuleBuildCachePath());
        await fse.emptydir(cachePath);
      }
      // sdk版本变更，需要删除build缓存
      if (
        oldModuleJson.app.compileSdkVersion &&
        this.sdkInfo.getToolchainsComponentVersion() &&
        oldModuleJson.app.compileSdkVersion !== this.sdkInfo.getToolchainsComponentVersion()
      ) {
        _log.debug('Clean the ArkTS cache due to SDK version is changed.');
        const cachePath: string = path.resolve(this.targetData.getPathInfo().getModuleBuildCachePath());
        await fse.emptydir(cachePath);
      }
    }

    // 将merged json写入文件
    fs.outputJSONSync(this._mergedModuleJson, mergedModuleOpt, { spaces: '\t' });
  }

  get isBundledDependencies(): boolean {
    return this.targetService.isBundledDependencies();
  }

  private computeAppJsonOpt(): AppOptObj {
    const appJson: AppOptObj = cloneDeep(this._appRes.getAppResOpt());

    // 不接受app.json5中multiProjects字段的配置，报warning
    if (appJson.app.multiProjects !== undefined) {
      _log.warn(
        'Field \'multiProjects\' should be configured in project\'s build-profile.json5. ' +
          'The value of this field configured in app.json5 will be ignored. ' +
          `Please configure this field in ${this._projectModel.getProfilePath()}.`,
      );
      delete appJson.app.multiProjects;
    }

    // har的app部分剔除与资源相关的字段，暂时也不需要
    if (this._isHarModule) {
      const tempAppJson = {
        app: {
          bundleName: appJson.app.bundleName,
          debug: appJson.app.debug,
          versionCode: appJson.app.versionCode,
          versionName: appJson.app.versionName,
          minCompatibleVersionCode: appJson.app.minCompatibleVersionCode,
          minAPIVersion: appJson.app.minAPIVersion,
          targetAPIVersion: appJson.app.targetAPIVersion,
          apiReleaseType: appJson.app.apiReleaseType,
          targetMinorAPIVersion: appJson.app.targetMinorAPIVersion,
          targetPatchAPIVersion: appJson.app.targetPatchAPIVersion
        },
      };
      if (this.targetService.getTargetData().getTargetName() === CommonConst.OHOS_TEST) {
        Object.assign(tempAppJson.app, {
          icon: appJson.app.icon,
          label: appJson.app.label,
          vendor: appJson.app.vendor,
          bundleType: appJson.app.bundleType
        });
      }
      appJson.app = tempAppJson.app;
    }
    if (this.asanEnable) {
      appJson.app.asanEnabled = this.asanEnable;
    }
    if (this.tsanEnable) {
      appJson.app.tsanEnabled = this.tsanEnable;
    }
    if (this.hwAsanEnable) {
      appJson.app.hwasanEnabled = this.hwAsanEnable;
    }
    if (this.ubsanEnable) {
      appJson.app.ubsanEnabled = this.ubsanEnable;
    }
    return appJson;
  }

  private mergeAppEnvironmentConfig(appOptObj: AppOptObj): void {
    const appEnvironmentObjs = this.parseToAppEnvironments(this.appEnvironments);
    if (appOptObj.app.appEnvironments !== undefined && appOptObj.app.appEnvironments.length !== 0) {
      _log.debug('Change app appEnvironment');
      const envMap = new Map<string, string>();
      appEnvironmentObjs.forEach((env) => {
        envMap.set(env.name, env.value);
      });
      appOptObj.app.appEnvironments?.forEach((env) => {
        envMap.set(env.name, env.value);
      });
      appOptObj.app.appEnvironments = Array.from(envMap, ([name, value]) => ({ name, value }));
    } else {
      _log.debug('Use cli appEnvironment');
      appOptObj.app.appEnvironments = appEnvironmentObjs;
    }
  }

  private processResourceStr(requestPermissions?: ModuleJson.RequestPermissionObj[]) {
    const pureHar = moduleDependencyUtil.getPureHar(this.service, this.moduleModel, this.projectModel, this.isFaMode);
    if (pureHar.has(this.moduleName)) {
      return;
    }

    if (this.moduleModel.isHspModule() || this.moduleModel.isHarModule()) {
      if (requestPermissions?.length) {
        this.generateResourceStrByPermissions(requestPermissions);
      }
      return;
    }

    this.initResourceStr();
  }

  private generateResourceStrByPermissions(requestPermissions: ModuleJson.RequestPermissionObj[]) {
    const resourceStr: string[] = [];
    requestPermissions.forEach((permission) => {
      if (permission.reason && !resourceStr.includes(permission.reason)) {
        resourceStr.push(permission.reason.replace('$string:', ''));
      }
    });
    if (!resourceStr.length) {
      return;
    }
    this.copyStringToResourceStr();
    this.initResourceStr();
    this.filterResStr(this.pathInfo.getResourceStr(), resourceStr);
  }

  private copyStringToResourceStr() {
    const dir = this._moduleTargetRes.getResourcePath();
    if (!fse.existsSync(dir)) {
      return;
    }
    fse.readdirSync(dir).forEach(fileName => {
      if (fileName === 'rawfile' || fileName === 'resfile') {
        return;
      }
      const src = path.resolve(this._moduleTargetRes.getResourcePath(), fileName, 'element', 'string.json');
      const dest = path.resolve(this.pathInfo.getResourceStr(), fileName, 'element', 'string.json');
      fse.ensureDirSync(path.dirname(dest));
      fse.copySync(src, dest);
    });
  }

  private filterResStr(resStrPath: string, resourceStr: string[]) {
    if (!fse.existsSync(resStrPath)) {
      return;
    }
    fse.readdirSync(resStrPath).forEach((fileName) => {
      if (fileName !== 'rawfile' && fileName !== 'resfile') {
        const filePath = path.resolve(resStrPath, fileName, 'element', 'string.json');
        const jsonData: { string: { name: string; value: string }[] } = {
          string: [],
        };
        if (fse.existsSync(filePath)) {
          const data = readJSONSync(filePath);
          data.string.forEach((item: { name: string; value: string }) => {
            if (resourceStr.includes(item.name)) {
              jsonData.string.push(item);
            }
          });
          if (jsonData.string.length === 0) {
            fse.removeSync(path.resolve(resStrPath, fileName));
            return;
          }
          const buildModeName = getBuildModeName();
          if (buildModeName === BuildModeConst.TEST) {
            fse.outputFileSync(
              path.resolve(
                this.pathInfo.getModuleBuildIntermediates(),
                BuildDirConst.INTERMEDIATES_RES,
                CommonConst.OHOS_TEST,
                BuildDirConst.RESOURCE_STR,
                fileName,
                'element',
                'string.json',
              ),
              JSON.stringify(jsonData),
            );
          }
          fse.outputFileSync(path.resolve(resStrPath, fileName, 'element', 'string.json'), JSON.stringify(jsonData));
        }
      }
    });
  }

  private mergeDslConfig(appOptObj: AppOptObj): void {
    if (this._bundleName) {
      appOptObj.app.bundleName = this._bundleName;
      _log.debug(replaceBundleName(`Change app bundleName with '${this._bundleName}'.`, this._bundleName));
    }
    if (this._multiProjects) {
      appOptObj.app.multiProjects = this._multiProjects;
      _log.debug(`Change app multiProjects with '${this._multiProjects}'.`);
    }
    if (this._vendor) {
      appOptObj.app.vendor = this._vendor;
      _log.debug(`Change app vendor with '${this._vendor}'.`);
    }
    appOptObj.app.apiReleaseType = this.sdkInfo.getReleaseType();
    _log.debug(`Change app api release type with '${this.sdkInfo.getReleaseType()}'`);
    this.resolveApi(appOptObj.app);
  }

  private resolveApi(app: AppJson.AppObj) {
    const apiMeta: ProjectBuildProfile.ProductApiMeta = this.targetData.getApiMeta();

    // 检查api配置项(compileSdkVersion, compatibleSdkVersion, targetSdkVersion)是否存在API9或以下, 如存在,
    // 因为compileSdkVersion >= targetSdkVersion >= compatibleSdkVersion, 则表明工程兼容API9设备
    // 因为API9及以下设备不识别大整数版本号(4.0.0(10) -> 40000010), 为了能够运行在API9设备上, 则使用API original数字形式, 如9, 10
    const toOriginalAPI = !![
      apiMeta.compileSdkVersion.version,
      apiMeta.compatibleSdkVersion.version,
      apiMeta.targetSdkVersion?.version ?? apiMeta.compileSdkVersion.version,
    ].find((version) => version <= ApiVersion.API_VERSION_9);

    const target = apiMeta.targetSdkVersion ?? apiMeta.compileSdkVersion;

    const targetAPI = this.targetData.isHarmonyOS() ? this.apiTransform(target) : target.version;
    const minAPI: string | number = this.targetData.isHarmonyOS() ? this.apiTransform(apiMeta.compatibleSdkVersion) : apiMeta.compatibleSdkVersion.version;

    app.compileSdkVersion = this.sdkInfo.getToolchainsComponentVersion();
    _log.debug(`Change app compile API version with '${this.sdkInfo.getToolchainsComponentVersion()}'`);
    app.targetAPIVersion = toOriginalAPI ? target.version : targetAPI;
    _log.debug(`Change app target API version with '${app.targetAPIVersion}'`);
    app.minAPIVersion = toOriginalAPI ? apiMeta.compatibleSdkVersion.version : minAPI;
    _log.debug(`Change app minimum API version with '${app.minAPIVersion}'`);
    app.compileSdkType = this.targetData.isHarmonyOS() ? 'HarmonyOS' : 'OpenHarmony';
    app.targetMinorAPIVersion = apiMeta.targetSdkVersion?.minor ?? 0;
    app.targetPatchAPIVersion = apiMeta.targetSdkVersion?.patch ?? 0;
  }

  /**
   * 获取所有需要移除的权限的名称集合，并提供重复告警
   * @param moduleOpt module对象
   * @return 返回需要移除的权限的名称集合
   */
  private getRemovePermissionSet(moduleOpt: ModuleOptObj): Set<string> {
    // 从moduleOptAfterMergeAllHarModuleOpt中去掉removePermission要求的permission，但不能错误删除本模块（hap、hsp）自带的permission
    const removePermissionList: Named[] = this.targetService.getBuildOption()?.removePermissions ?? [];
    // 使用map方法获取所有权限的名称，确保返回的类型是字符串集合
    const removePermissionNameSet = new Set(removePermissionList.map((item: Named) => item.name as string));

    // 执行removePermission添加
    if (removePermissionNameSet?.size > 0 && moduleOpt?.module?.requestPermissions !== undefined) {
      const necessaryPermissions: string[] = [];
      moduleOpt.module.requestPermissions.forEach((permission) => {
        if (removePermissionNameSet.has(permission.name)) {
          necessaryPermissions.push(permission.name);
          removePermissionNameSet.delete(permission.name);
        }
      });

      // 警告自身模块和removePermission配置相冲突的权限
      if (necessaryPermissions.length > 0) {
        _log.warn(
          `The permissions ${necessaryPermissions.join(
            ', ',
          )} being removed in the removePermission configuration are duplicated with those in the module.json files of the hap/hsp source code modules. These permissions belong to the current module and should not be removed`,
        );
      }
    }
    return removePermissionNameSet;
  }
  /**
   * 获取依赖三方包中module文件转换得到的json对象列表，依赖包括本地或远程hsp、har，合并其中module文件json对象的部分内容
   * @param moduleOpt 本模块module文件的json对象
   * @private
   */
  private mergeAllHarModuleOpt(moduleOpt: ModuleOptObj): ModuleOptObj {
    // har不需要合并依赖的字节码har中的module.json5
    if (this._isHarModule) {
      // 如果自身为har 合并其所依赖的har的metadata和dependency
      this.mergeBundledDependenciesHar(moduleOpt);
      return moduleOpt;
    }

    // 获取依赖三方包中module文件转换得到的json对象列表，依赖包括本地或远程hsp、har
    const harModuleOpts = this.transformJson2Opts();

    let tempMergedMetaData: Named[] | undefined = moduleOpt.module.metadata;
    let tempRequestPermission: Named[] | undefined = moduleOpt.module.requestPermissions;

    for (let index = 0; index < harModuleOpts.length; index++) {
      const harModuleOpt: HarModuleOpt = harModuleOpts[index];

      if (harModuleOpt !== null) {
        this.checkHarApiStatus(harModuleOpt);

        // har包不校验设备类型
        if (harModuleOpt.module.type !== ModuleType.Har) {
          checkHarDeviceTypes(moduleOpt, harModuleOpt, this.targetService);
        }

        // 合并依赖的本地或远程hsp、har的module中的metadata和requestPermission
        tempMergedMetaData = this.mergeArrayOptions(tempMergedMetaData, harModuleOpt.module.metadata, harModuleOpt.module.name);
        tempRequestPermission = this.mergeHarRequestPermission(tempRequestPermission, harModuleOpt);
      }
    }
    if (tempMergedMetaData?.length !== 0) {
      moduleOpt.module.metadata = tempMergedMetaData as unknown[] as MetadataObj[];
    }
    if (tempRequestPermission?.length !== 0) {
      moduleOpt.module.requestPermissions = tempRequestPermission as unknown[] as RequestPermissionObj[];
    }

    return moduleOpt;
  }

  /**
   * bundledDependencies为true时需要合并其依赖har的module.json5中的metadata和requestPermission
   * @param moduleOpt
   * @private
   */
  private mergeBundledDependenciesHar(moduleOpt: ModuleOptObj): void {
    if (!this.isBundledDependencies) {
      return;
    }

    const metadata = moduleOpt.module.metadata ?? [];
    const requestPermissions = moduleOpt.module.requestPermissions ?? [];

    this.service.getHarDependencies().forEach((dependency) => {
      if (dependency.isByteCodeHarDependency()) {
        // 字节码har过滤
        return;
      }

      const dependencyModuleJsonPath = path.resolve(
        dependency.getDependencyRootPath(),
        BuildDirConst.SRC,
        BuildDirConst.MAIN,
        dependency.isLocal() ? CommonConst.MODULE_JSON5 : CommonConst.MODULE_JSON,
      );
      const depModuleJsonObj: ModuleOptObj = getJson5Obj(dependencyModuleJsonPath);

      // 收集依赖源码har的metadata
      const depMetadata = depModuleJsonObj.module.metadata;
      this.deDuplicateArr(depMetadata, metadata);

      // 收集依赖源码har的requestPermission
      const depRequestPermissions = depModuleJsonObj.module.requestPermissions;
      this.deDuplicateArr(depRequestPermissions, requestPermissions);
    });

    Object.assign(moduleOpt.module, { metadata });
    Object.assign(moduleOpt.module, { requestPermissions });
  }

  /**
   * metadata与requestPermissions需要去重
   * 1.参考hap/hsp规则.若字节码har依赖源码har出现重复name对象:保留字节码har的配置,去除依赖源码har的重复项
   * 2.若依赖源码har之间出现重复name对象:仅收集初次遍历得到的配置项
   * @param depModuleConfigArr 依赖har的metadata或requestPermissions对象
   * @param moduleConfigArr 字节码har的metadata或requestPermissions对象
   * @private
   */
  private deDuplicateArr(
    depModuleConfigArr: (ModuleJson.MetadataObj | ModuleJson.RequestPermissionObj)[] | undefined,
    moduleConfigArr: (ModuleJson.MetadataObj | ModuleJson.RequestPermissionObj)[],
  ): void {
    depModuleConfigArr?.forEach((depModuleConfig) => {
      if (
        moduleConfigArr.some((config) => {
          return config.name === depModuleConfig.name;
        })
      ) {
        return;
      }

      moduleConfigArr.push(depModuleConfig);
    });
  }

  /**
   * 合并三方依赖库， 包括本地依赖和远程依赖
   * @param jsonType 用于指定json文件类型，比如config或module
   * @return 返回一个字符串数组，包含了合并后的三方依赖库路径
   */
  protected mergeDependsLibs(jsonType: string): string[] {
    // 获取三方依赖的module.json，包含hsp和har
    const ohDeps: string[] = this.service.getDependencyRootPaths();
    const localHarModuleMap = this.service.getHarModuleDependenciesWithRootPath();
    const localHarModulePathList = this.service.getHarModuleDependencyPaths();
    const localHspModuleList = this.service.getHspModuleDependencies();
    const localHspModulePathList = this.service.getHspModuleDependencyPaths();
    const depHspTargets: Map<string, ModuleTargetData> = HspTargetUtils.calDepHspTargets(this.targetService);
    return ohDeps.map((ohDepPath) => {
      // 根据用户配置的配置文件，获取本地依赖的build目录 如果没配置 则返回默认的路径
      // 如果是使用的本地依赖 则使用这个路径而不是ohDepPath
      const buildCacheParentDir = InjectUtil.getBuildCacheParentDir(
        ohDepPath,
        path.join(hvigorCore.getProject()!.getName(), ohDepPath.substring(hvigorCore.getProject()!.getNodeDir().length)),
      );

      if (localHspModulePathList.includes(ohDepPath)) {
        const hspModuleName = (localHspModuleList.find(([, dependency]) => {
          return dependency.getDependencyRootPath() === ohDepPath;
        }) ?? [''])[0];
        const depHspTarget = depHspTargets.get(hspModuleName);
        return path.resolve(
          buildCacheParentDir,
          this._buildRoot,
          this.targetData.getProduct().name,
          BuildDirConst.INTERMEDIATES,
          BuildDirConst.INTERMEDIATES_MERGE_PROFILE,
          depHspTarget?.getTargetName() ?? DefaultTargetConst.DEFAULT_TARGET,
          jsonType,
        );
      } else if (localHarModulePathList.includes(ohDepPath)) {
        return this.getHarDenPath(localHarModuleMap, ohDepPath, buildCacheParentDir, jsonType);
      }
      return path.resolve(ohDepPath, BuildDirConst.SRC, BuildDirConst.MAIN, jsonType);
    });
  }

  /**
   * 将三方依赖的module文件转换为ModuleOpt对象数组，不止har依赖，还包括hsp
   * @return {HarModuleOpt[]} 返回ModuleOpt对象数组，不止har依赖，还包括hsp，不要被命名误导
   */
  private transformJson2Opts() {
    const depModuleOpts: HarModuleOpt[] = this._ohLibs.map((depModuleJson) => {
      if (fs.pathExistsSync(depModuleJson)) {
        return getJson5Obj(depModuleJson);
      } else {
        _log.warn(
          `${depModuleJson} does not exist. This library will not be merged. This may occur ` +
            'when the project is manually modified and the local library is not declared in ' +
            `${this._projectModel.getProfilePath()}. Please confirm the correctness of this module. ` +
            `You may try to solve the problem by declare it in ${this.moduleModel.getName()} build-profile.json5.`,
        );
        return null;
      }
    });
    return depModuleOpts;
  }

  /**
   * 将依赖的三方包json对象的requestPermissions合并到tempMergedRequestPermission中
   * @param tempMergedRequestPermission 已合并的临时RequestPermission列表
   * @param harModuleOpt 依赖的三方包json对象
   * @private
   */
  private mergeHarRequestPermission(tempMergedRequestPermission: Named[] | undefined, harModuleOpt: HarModuleOpt) {
    return this.mergeArrayOptions(tempMergedRequestPermission, harModuleOpt.module.requestPermissions);
  }

  private mergeArrayOptions(highPriorityOptions: Named[] | undefined, lowPriorityOptions: Named[] | undefined, harModuleName?: string): Named[] {
    const result: Named[] = [];
    const keys: MetaDataNameType[] = [];

    highPriorityOptions?.forEach((highPriorityOption) => {
      result.push(highPriorityOption);

      // 高优先级中的MetaData如果有name为undefined或者为空只会来源于hap包自身,此时保留
      keys.push(highPriorityOption.name);
    });

    lowPriorityOptions?.forEach((lowPriorityOption) => {
      const lowPriorityOptionName = lowPriorityOption.name;
      if (lowPriorityOptionName !== undefined && lowPriorityOptionName !== '') {
        if (!keys.includes(lowPriorityOption.name!)) {
          result.push(lowPriorityOption);
        }
      } else {
        _log.warn(
          `The metadata configuration in module.json5 whose name is empty or undefined in the har:${harModuleName}.
        This configuration will not be merged.`,
        );
      }
    });
    return result;
  }

  private checkHarApiStatus(harModuleOpt: HarModuleOpt): void {
    let harMinApiVersion = harModuleOpt.app.minAPIVersion;
    const harModuleName = harModuleOpt.module.name;
    if (harMinApiVersion === undefined) {
      _log.printErrorExit('EMPTY_MIN_API_VERSION', [`Har module ${harModuleName}`],[[`Har module ${harModuleName}`]]);
    }
    if (harMinApiVersion!.toString().length >= 4) {
      harMinApiVersion = this.harMinApiVersionToAPi(harMinApiVersion!.toString());
    }
    if (this.targetData.getCompatibleApiVersion() < parseInt(harMinApiVersion)) {
      _log.printErrorExit('HAR_UNSUPPORTED_API_VERSION', [this.compatibleApiVersion, harMinApiVersion, harModuleName]);
    }
  }

  // 如果harMinApiVersion为api转码后的内容，则还原为对应的api，如'40000010'转为10
  private harMinApiVersionToAPi(harMinApiVersion: string) {
    const length = harMinApiVersion.length;
    const lastString = harMinApiVersion.substring(length - 3, length);
    return parseInt(lastString);
  }

  /**
   * 定制处理product中部分字段配置
   * @param mergedModuleOpt
   * @param curProduct
   * @private
   */
  private customizeModuleOpt(mergedModuleOpt: AppOptObj & ModuleOptObj, curProduct: ProductBuildOpt) {
    const appOptFromConfig: AppOpt = cloneDeep(projectOhosConfigManager.getOverrides()?.appOpt);
    const mergedModuleOptObj = mergedModuleOpt.module;
    const curBundleType = appOptFromConfig?.bundleType ?? curProduct.bundleType ?? mergedModuleOpt.app.bundleType ?? 'app';

    const mergedAppOptObj = mergedModuleOpt.app;
    mergedAppOptObj.bundleType = curBundleType;
    if (!this._isHarModule) {
      mergedAppOptObj.label = appOptFromConfig?.label ?? curProduct.label ?? mergedAppOptObj.label;
      mergedAppOptObj.icon = appOptFromConfig?.icon ?? curProduct.icon ?? mergedAppOptObj.icon;
    }
    mergedAppOptObj.versionCode = appOptFromConfig?.versionCode ?? curProduct.versionCode ?? mergedAppOptObj.versionCode;
    mergedAppOptObj.versionName = appOptFromConfig?.versionName ?? curProduct.versionName ?? mergedAppOptObj.versionName;

    // installationFree字段配置不依赖bundleType,atomicService置其为true,其余置为false
    if (curBundleType === BundleType.ATOMIC_SERVICE) {
      // 若当前工程的bundleType为atomicService则在产物module.json中写入模块target的atomicService配置,若不存在则写入module.json中的atomicService配置
      mergedModuleOptObj.atomicService = this.targetData.getTargetOpt().config?.atomicService ?? mergedModuleOptObj.atomicService;
      mergedModuleOptObj.installationFree = true;
    }

    if (curBundleType === BundleType.APP || curBundleType === BundleType.SHARED) {
      mergedModuleOptObj.installationFree = false;
      if (mergedModuleOptObj.atomicService) {
        // bundleType为app或shared时删除所有atomicService配置
        delete mergedModuleOptObj.atomicService;
      }
    }

    // 为产物添加 buildMode 字段
    mergedAppOptObj.buildMode = this.targetService.isDebug() ? BuildModeConst.DEBUG : BuildModeConst.RELEASE;

    // 在没有app.json5中设置debug时，设置debug字段默认值
    if (mergedAppOptObj.debug === undefined) {
      mergedAppOptObj.debug = this.targetService.isDebug();
    }
  }

  private mergeBuildProfileAbilities(moduleOpt: ModuleOptObj): ModuleOptObj {
    // har包、buildProfile里abilities为空，以及module.json里为空都不执行merge
    if (!this.moduleModel.isHapModule() || !this._moduleBuildAbilities || !moduleOpt.module.abilities) {
      if (this.moduleModel.isHspModule() && this._moduleBuildAbilities) {
        _log.warn('hsp module does not support custom icon/label/lauchType in buildProfile');
      }
      return moduleOpt;
    }
    const names: string[] = moduleOpt.module.abilities.map((ability) => ability.name);
    for (const moduleBuildAbility of this._moduleBuildAbilities) {
      for (const moduleAbility of moduleOpt.module.abilities) {
        // 如果build-profile中的ability不存在，打印warn
        if (!moduleBuildAbility.name || !names.includes(moduleBuildAbility.name)) {
          _log.warn(
            `The ability name :${moduleBuildAbility.name} in build-profile can't find correspond ability name
            in module.json.`,
          );
          continue;
        }
        if (moduleAbility.name === moduleBuildAbility.name) {
          moduleAbility.icon = moduleBuildAbility.icon ?? moduleAbility.icon;
          moduleAbility.label = moduleBuildAbility.label ?? moduleAbility.label;
          moduleAbility.launchType = moduleBuildAbility.launchType ?? moduleAbility.launchType;
        }
      }
    }
    return moduleOpt;
  }

  protected initResourceStr() {
    const harAndHspDependencies: Dependency[] = [...this.service.getHspDependencies(), ...this.service.getHarDependencies()];
    harAndHspDependencies.forEach((dependency) => {
      let resourceStr: string;
      if (dependency.isLocal()) {
        resourceStr = this.pathInfo.getDependencyResourceStr(dependency.getDependencyRootPath());
      } else if (dependency.getDependencyType() === DependencyType.DEPENDENCY_TYPE_HAR) {
        resourceStr = path.resolve(dependency.getDependencyRootPath(), 'src', 'main', 'resources');
      } else {
        resourceStr = path.resolve(dependency.getDependencyRootPath(), 'src', 'main', 'resource');
      }
      if (fse.existsSync(resourceStr)) {
        fse.readdirSync(resourceStr).forEach((fileName) => {
          if (fileName !== 'rawfile' && fileName !== 'resfile') {
            const dependencyFilePath = path.resolve(resourceStr, fileName, 'element', 'string.json');
            const moduleFilePath = path.resolve(this.pathInfo.getResourceStr(), fileName, 'element', 'string.json');
            let moduleFileJson: any;
            const dependencyFileJson = getJson5Obj(dependencyFilePath);
            if (dependencyFileJson && !Object.prototype.hasOwnProperty.call(dependencyFileJson, 'string')) {
              _log.printErrorExit('WRONG_STRING_JSON_FORMAT', [dependencyFilePath]);
            }
            if (fse.existsSync(moduleFilePath)) {
              moduleFileJson = getJson5Obj(moduleFilePath);
              if (!Object.prototype.hasOwnProperty.call(moduleFileJson, 'string')) {
                _log.printErrorExit('WRONG_STRING_JSON_FORMAT', [moduleFilePath]);
              }
              moduleFileJson = mergeStringJson(moduleFileJson, dependencyFileJson);
            } else {
              moduleFileJson = dependencyFileJson;
            }
            if (moduleFileJson) {
              fse.outputFileSync(moduleFilePath, JSON.stringify(moduleFileJson));
            }
          }
        });
      }
    });
  }
}
function checkHarDeviceTypes(moduleOpt: ModuleOptObj, harModuleOpt: HarModuleOpt, targetService: TargetTaskService) {
  const moduleDeviceTypes = targetService.getTargetData().getTargetDeviceType();
  const harModuleDeviceTypes = harModuleOpt.module.deviceTypes;

  const deviceTypeDefault = 'default';
  const deviceTypePhone = 'phone';

  // 目前default和phone等价
  if (harModuleDeviceTypes.includes(deviceTypeDefault) || harModuleDeviceTypes.includes(deviceTypePhone)) {
    harModuleDeviceTypes.push(deviceTypePhone);
    harModuleDeviceTypes.push(deviceTypeDefault);
  }

  const diffDeviceTypes = moduleDeviceTypes.filter((deviceType) => {
    return !harModuleDeviceTypes.includes(deviceType);
  });

  if (diffDeviceTypes.length > 0) {
    _log.printErrorExit('CURRENT_MODULE_NOT_SUPPORT_THE_DEVICE_TYPE', [harModuleOpt.module.type, harModuleOpt.module.name, diffDeviceTypes.join(', ')]);
  }
}
